import type { Plugin, PluginContext, Command, CLIConfig, Logger } from '../types/index.js';
import { CommandRegistry } from './command.js';

export class PluginManager {
  private plugins: Map<string, Plugin> = new Map();
  private commandRegistry: CommandRegistry;
  private config: CLIConfig;
  private logger: Logger;

  constructor(commandRegistry: CommandRegistry, config: CLIConfig, logger: Logger) {
    this.commandRegistry = commandRegistry;
    this.config = config;
    this.logger = logger;
  }

  async loadPlugin(plugin: Plugin): Promise<void> {
    try {
      this.logger.debug(`Loading plugin: ${plugin.name}`);

      if (this.plugins.has(plugin.name)) {
        throw new Error(`Plugin ${plugin.name} is already loaded`);
      }

      // Install plugin if needed
      if (plugin.install) {
        await plugin.install();
      }

      // Create plugin context
      const context: PluginContext = {
        registerCommand: (command: Command) => {
          this.commandRegistry.register(command);
          this.logger.debug(`Registered command: ${command.name} from plugin: ${plugin.name}`);
        },
        unregisterCommand: (name: string) => {
          this.commandRegistry.unregister(name);
          this.logger.debug(`Unregistered command: ${name} from plugin: ${plugin.name}`);
        },
        getConfig: () => this.config,
        getLogger: () => this.logger,
      };

      // Register plugin commands
      for (const command of plugin.commands) {
        this.commandRegistry.register(command);
      }

      // Activate plugin
      if (plugin.activate) {
        await plugin.activate(context);
      }

      this.plugins.set(plugin.name, plugin);
      this.logger.success(`Plugin loaded successfully: ${plugin.name}`);
    } catch (error) {
      this.logger.error(`Failed to load plugin ${plugin.name}:`, error);
      throw error;
    }
  }

  async unloadPlugin(name: string): Promise<void> {
    const plugin = this.plugins.get(name);
    if (!plugin) {
      throw new Error(`Plugin ${name} is not loaded`);
    }

    try {
      this.logger.debug(`Unloading plugin: ${name}`);

      // Deactivate plugin
      if (plugin.deactivate) {
        await plugin.deactivate();
      }

      // Unregister plugin commands
      for (const command of plugin.commands) {
        this.commandRegistry.unregister(command.name);
      }

      // Uninstall plugin if needed
      if (plugin.uninstall) {
        await plugin.uninstall();
      }

      this.plugins.delete(name);
      this.logger.success(`Plugin unloaded successfully: ${name}`);
    } catch (error) {
      this.logger.error(`Failed to unload plugin ${name}:`, error);
      throw error;
    }
  }

  getPlugin(name: string): Plugin | undefined {
    return this.plugins.get(name);
  }

  hasPlugin(name: string): boolean {
    return this.plugins.has(name);
  }

  listPlugins(): Plugin[] {
    return Array.from(this.plugins.values());
  }

  getPluginNames(): string[] {
    return Array.from(this.plugins.keys());
  }

  async reloadPlugin(name: string): Promise<void> {
    const plugin = this.plugins.get(name);
    if (!plugin) {
      throw new Error(`Plugin ${name} is not loaded`);
    }

    await this.unloadPlugin(name);
    await this.loadPlugin(plugin);
  }

  async loadPluginsFromConfig(): Promise<void> {
    if (!this.config.plugins || this.config.plugins.length === 0) {
      this.logger.debug('No plugins configured to load');
      return;
    }

    for (const pluginPath of this.config.plugins) {
      try {
        this.logger.debug(`Loading plugin from: ${pluginPath}`);
        
        // Dynamic import of plugin
        const pluginModule = await import(pluginPath);
        const plugin: Plugin = pluginModule.default || pluginModule;

        if (!this.isValidPlugin(plugin)) {
          throw new Error(`Invalid plugin structure: ${pluginPath}`);
        }

        await this.loadPlugin(plugin);
      } catch (error) {
        this.logger.error(`Failed to load plugin from ${pluginPath}:`, error);
      }
    }
  }

  private isValidPlugin(plugin: any): plugin is Plugin {
    return (
      plugin &&
      typeof plugin.name === 'string' &&
      typeof plugin.version === 'string' &&
      typeof plugin.description === 'string' &&
      Array.isArray(plugin.commands)
    );
  }

  async validatePlugin(plugin: Plugin): Promise<boolean> {
    try {
      // Check basic structure
      if (!this.isValidPlugin(plugin)) {
        return false;
      }

      // Check commands
      for (const command of plugin.commands) {
        if (!command.name || !command.description || typeof command.execute !== 'function') {
          return false;
        }
      }

      return true;
    } catch {
      return false;
    }
  }

  getPluginInfo(name: string): { name: string; version: string; description: string; commands: string[] } | null {
    const plugin = this.plugins.get(name);
    if (!plugin) {
      return null;
    }

    return {
      name: plugin.name,
      version: plugin.version,
      description: plugin.description,
      commands: plugin.commands.map(cmd => cmd.name),
    };
  }
}

export const createPluginManager = (
  commandRegistry: CommandRegistry,
  config: CLIConfig,
  logger: Logger
): PluginManager => {
  return new PluginManager(commandRegistry, config, logger);
};
